﻿using Com.Lancetinc.Evs.Cameras;
using Com.Lancetinc.Evs.Data;
using Com.Lancetinc.Evs.Events;
using Com.Lancetinc.Evs.Helpers;
using Com.Lancetinc.Evs.Properties;
using Com.Lancetinc.Evs.Services;
using ControlzEx.Theming;
using MahApps.Metro.Theming;
using NLog;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
using MessageBox = System.Windows.MessageBox;

namespace Com.Lancetinc.Evs
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    public partial class App
    {
        public App()
        {
            InitializeComponent();
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            RegisterUnhandledExceptionHandlers();

            base.OnStartup(e);
            try
            {
                var sw = Stopwatch.StartNew();
                JsonUtil.Init();
                SqlDataAccessor.Initialize(Settings.Default.DbSetting);

                InitTheme();
                sw.Stop();
                MainWindow = new MainWindow();
                MainWindow.Show();

                EventsListener.Startup();
                AfterStartup();
            }
            catch (InvalidOperationException)
            {
                // 登录页面直接关闭会出错
            }
            catch (Exception exc)
            {
                LogManager.GetCurrentClassLogger().Error(exc);
                MessageBox.Show(exc.Message, AppConsts.AppTitle, MessageBoxButton.OK, MessageBoxImage.Error);
                this.Shutdown(-1);
            }
        }

        #region 异常处理

        private void RegisterUnhandledExceptionHandlers()
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
            Dispatcher.UnhandledException += Dispatcher_UnhandledException;
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            LogManager.GetCurrentClassLogger().Error(e.ExceptionObject);
            var exc = e.ExceptionObject as Exception;
            var msg = exc != null ? exc.Message : e.ExceptionObject.ToString();
            if (exc != null && !ShouldShowExcErrorMsg(exc))
                return;

            AppStatusManager.Default.AppStatus.ErrorMessage = msg;
            MessageBox.Show(msg, AppConsts.AppTitle, MessageBoxButton.OK, MessageBoxImage.Error);
        }

        private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            e.SetObserved();
            LogManager.GetCurrentClassLogger().Error(e.Exception);
        }

        private void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            e.Handled = true;
            LogManager.GetCurrentClassLogger().Error(e.Exception);
            if (!ShouldShowExcErrorMsg(e.Exception))
                return;

            AppStatusManager.Default.AppStatus.ErrorMessage = e.Exception.Message;
            MessageBox.Show(e.Exception.Message, AppConsts.AppTitle, MessageBoxButton.OK, MessageBoxImage.Error);
        }

        private static bool ShouldShowExcErrorMsg(Exception exc)
        {
            if (exc.Message == null)
                return false;
            if (exc.Message.Contains("MFCreateDeviceSource failed:"))
                return false; // RadWebCam_Loaded的时候插入摄像头会报这个错,MFCreateDeviceSource failed: MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED
            return true;
        }
        #endregion

        #region Init Theme

        private void InitTheme()
        {
            var darkTheme = ThemeManager.Current.AddLibraryTheme(
                  new LibraryTheme(
                      new Uri("pack://application:,,,/Leinzett;component/Themes/Styles/Themes/Dark.Navy.xaml"),
                      MahAppsLibraryThemeProvider.DefaultInstance
                  )
              );

            var lightTheme = ThemeManager.Current.AddLibraryTheme(
                new LibraryTheme(
                    new Uri("pack://application:,,,/Leinzett;component/Themes/Styles/Themes/Light.Navy.xaml"),
                    MahAppsLibraryThemeProvider.DefaultInstance
                )
            );

            ThemeManager.Current.ChangeTheme(this, lightTheme);
        }

        #endregion

        #region single instance

        private IntPtr HandleMessages(IntPtr handle, int message, IntPtr wParameter, IntPtr lParameter,
            ref bool handled)
        {
            if (message == Program.SingleInstanceMsgId)
            {
                if (MainWindow.WindowState == WindowState.Minimized)
                {
                    MainWindow.WindowState = WindowState.Normal;
                }

                MainWindow.Topmost = true;
                MainWindow.Topmost = false;
            }

            return IntPtr.Zero;
        }

        private void AfterStartup()
        {
            HwndSource.FromHwnd(new WindowInteropHelper(MainWindow).Handle)?.AddHook(HandleMessages);
        }

        #endregion

        #region 测试用
        [Conditional("DEBUG")]
        private void GenTestData()
        {
            var logger = LogManager.GetCurrentClassLogger();
            var exeDir = DirUtil.ExeDirectory;
            var licFile = Path.Combine(exeDir, "license_code.dat");
            if (!File.Exists(licFile))
            {
                var finalCode = Md5Utils.Encrypt(AuthorizationHelper.GetCpuId());
                File.WriteAllText(licFile, finalCode);
            }

            var configLines = AppConfigHelper.LoadConfig().ToList();
            if (configLines.Any(x => x.StartsWith("v=")))
            {
                logger.Info("video exists");
                return;
            }

            var cameras = CameraLib.EnumVideoDevice();
            if (cameras.Length == 0)
            {
                logger.Warn("no cameras");
                return;
            }

            foreach (var ci in cameras)
            {
                var line = GenVideoConfigLine(ci.Pid, ci.Vid);
                configLines.Add(line);
            }
            var cfgPath = AppDomain.CurrentDomain.BaseDirectory;
            cfgPath = Path.Combine(cfgPath, "config.ini");

            File.WriteAllLines(cfgPath, configLines);

            logger.Info("write config success");
        }

        private static string GenVideoConfigLine(int pid, int vid)
        {
            var sb = new StringBuilder();
            sb.Append("A1");
            sb.Append(",").Append(pid);
            sb.Append(",").Append(vid);
            var crc = Md5Utils.Encrypt(sb.ToString()).Substring(10, 5);
            sb.Append(",").Append(crc);
            return "v=" + sb;
        }

        /// <summary>
        /// 比较中英文的key是否一致
        /// </summary>
        [Conditional("DEBUG")]
        private void CompareLang()
        {
            var dir = new DirectoryInfo(DirUtil.ExeDirectory);
            string levsDir = null;
            do
            {
                if (dir.Parent == null)
                    break;
                if (dir.Parent.Name.EndsWith("levs", StringComparison.OrdinalIgnoreCase))
                {
                    levsDir = dir.Parent.FullName;
                    break;
                }

                dir = dir.Parent;
            } while (dir != null);

            if (string.IsNullOrEmpty(levsDir))
                return;

            var cnRes = Path.Combine(levsDir, "messages_cn.ini");
            var enRes = Path.Combine(levsDir, "messages_en.ini");
            if (!File.Exists(cnRes) || !File.Exists(enRes))
                return;

            var cnKeys = I18nHelper.ReadResourceFile(cnRes).Keys.ToHashSet();
            var enKeys = I18nHelper.ReadResourceFile(enRes).Keys.ToHashSet();
            // 英文资源中没有
            var notExistsInEn = cnKeys.Except(enKeys).ToList();
            // 中文资源中没有
            var notExistsInCn = enKeys.Except(cnKeys).ToList();

            Debug.Assert(notExistsInCn.Count == 0);
            Debug.Assert(notExistsInEn.Count == 0);
        }
        #endregion
    }
}